package com.wang.gulimall.ware.listener;

import com.rabbitmq.client.Channel;
import com.wang.common.to.OrderTo;
import com.wang.common.to.mq.StockLockedTo;
import com.wang.gulimall.ware.service.WareSkuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
 * @Description: 40分钟之后用户没有支付的话库存自动解锁 解决少卖的问题
 * @Created: with IntelliJ IDEA.
 * @author: 夏沫止水
 * @createTime: 2020-07-07 00:20
 **/

@Slf4j
@RabbitListener(queues = "stock.release.stock.queue")
@Service
public class StockReleaseListener {

    @Autowired
    private WareSkuService wareSkuService;

    /**
     * 1、库存自动解锁  库存服务延时于订单服务之后触发解锁服务，根据订单表进行解锁
     * 下订单成功，库存锁定成功，接下来的业务调用失败，导致订单回滚。之前锁定的库存就要自动解锁
     * 2、订单失败
     * 库存锁定失败
     * 只要解锁库存的消息失败，一定要告诉服务解锁失败
     */
    @RabbitHandler //补偿逻辑40分钟之后解锁，订单服务30分钟之后关闭订单
    //@Retryable(value = Exception.class, maxAttempts = 5, backoff = @Backoff(delay = 2000, multiplier = 1.5))
    public void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {
        log.error("库存服务接收到消息队列的消息");
        try {
            //当前消息是否被第二次及以后（重新）派发过来了
            // Boolean redelivered = message.getMessageProperties().getRedelivered();
            //解锁库存
            log.error("调用解锁库存服务");
            wareSkuService.unlockStock(to);
            // 手动删除消息
            log.error("手动删除消息");
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            // 解锁失败 将消息重新放回队列，让别人消费
            log.error("出现异常拒绝签收");
            //BasicReject方法第一个参数是消息的DeliveryTag，对于每个Channel来说，
            // 每个消息都会有一个DeliveryTag，一般用接收消息的顺序来表示：1,2,3,4 等等。
            // 第二个参数是是否放回queue中，requeue。
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }
    }

    // 订单解锁就解锁库存 主动逻辑
    //30分钟后订单服务发送解锁成功的订单消息，库存服务开始解锁对应的库存 主动逻辑
    //订单服务主动关闭订单发送消息给库存服务的MQ, 库存服务监听主动关闭库存服务,防止因为网络延迟导致库存没有解锁
    @RabbitHandler
    public void handleOrderCloseRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {
        log.error("库存服务受到订单服务的关闭订单消息，准备解锁库存");
        try {
            wareSkuService.unlockStock(orderTo);
            // 手动删除消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            // 解锁失败 将消息重新放回队列，让别人消费
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }
    }
}
